/** 
* @file		adc.c
* @date		November 22, 2012
* @brief	ADC configuration and support functions
* @see		TIM/TIM_ADC_Trigger/main.c
*/

#include "adc.h"


uint16_t ADC_Value;
uint16_t ADC_Buffer[2*ADC_BUF_SIZE];

static uint32_t ADC_BufferSize = 0;


/**
* @brief   		Configure ADC1 in hardware triggering mode
* @param  		channel		index from 0-3
* @retval 		None
*/
void ADC_Config(uint32_t channel) {
	ADC_InitTypeDef ADC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
  /* Enable clocks for ADC1, GPIOC and DMA1 */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_DMA1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
		
	/* Configure ADC Channel 1x as analog input */	
	GPIO_StructInit(&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	switch(channel) {
		case 0:
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
			break;
		case 1:
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
			break;
		case 2:
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
			break;
		case 3:
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
			break;
		default:
			break;
  }
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* ADCs DeInit */  
  ADC_DeInit(ADC1);
  ADC_StructInit(&ADC_InitStructure);
  
  /* Configure the ADC1 in T1CC4 triggered mode with 12-bit resolution */
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; 
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;    
  ADC_InitStructure.ADC_ExternalTrigConv =  ADC_ExternalTrigConv_T1_CC4;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;
  ADC_Init(ADC1, &ADC_InitStructure); 
  
  /* Convert the ADC1 Channel 1x with 13.5 Cycles as sampling time */ 
	switch(channel) {
		case 0:
			ADC_ChannelConfig(ADC1, ADC_Channel_10 , ADC_SampleTime_13_5Cycles);
			break;
		case 1:
			ADC_ChannelConfig(ADC1, ADC_Channel_11 , ADC_SampleTime_13_5Cycles);
			break;
		case 2:
			ADC_ChannelConfig(ADC1, ADC_Channel_12 , ADC_SampleTime_13_5Cycles);
			break;
		case 3:
			ADC_ChannelConfig(ADC1, ADC_Channel_13 , ADC_SampleTime_13_5Cycles);
			break;
		default:
			break;
  }
  
  /* ADC Calibration */
  ADC_GetCalibrationFactor(ADC1);
  
  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);     
  while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); 
}


/**
* @brief   		Enable end-of-conversion IRQ for 1-point sampling mode 
* @param  		None
* @retval 		None
*/
void ADC_EnableIRQ() {
	NVIC_InitTypeDef NVIC_InitStructure;

	/* Stop and reset DMA and DMA IRQ operations */
  ADC_ITConfig(ADC1, ADC_IT_EOC, DISABLE);
	DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, DISABLE);
	DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, DISABLE);
	ADC_StopOfConversion(ADC1);
	DMA_DeInit(DMA1_Channel1);
	ADC_DMACmd(ADC1, DISABLE);
	
  /* Configure and enable ADC1 interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = ADC1_COMP_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);	

  ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
	ADC_StartOfConversion(ADC1);
}


/**
* @brief   		Enable DMA and transfer-complete IRQ for N-point sampling mode
* @param  		None
* @retval 		None
*/
void ADC_EnableDMA(uint32_t count) {	
	NVIC_InitTypeDef NVIC_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	
	ADC_BufferSize = count;
	
	/* Disable ADC1 EOC IRQ */
  ADC_ITConfig(ADC1, ADC_IT_EOC, DISABLE);
	DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, DISABLE);	
	DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, DISABLE);	
	ADC_StopOfConversion(ADC1);
	
	/* Configure DMA for ADC1 (channel1) with  */
	DMA_DeInit(DMA1_Channel1);
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Buffer;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = 2 * ADC_BufferSize; // double buffer
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);

	/* Enable DMA1 Channel1 and ADC DMA request in oneshot mode */
	DMA_Cmd(DMA1_Channel1, ENABLE);
	ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);
	ADC_DMACmd(ADC1, ENABLE);

  /* Configure and enable ADC1 interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);	

	DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, ENABLE);
	DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
	ADC_StartOfConversion(ADC1);
}


/**
* @brief   		ADC EOC Handler - get value, then call hook function
* @param  		None
* @retval 		None
*/
void ADC1_COMP_IRQHandler() {
	/* Get ADC converted value */
	ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
	ADC_Value = ADC_GetConversionValue(ADC1);
	
	/* Hook function */
	ADC_HookFunc_ADC_IT_EOC();
}


/**
* @brief   		DMA HT/TC Handler - invoke HC/TC hook function
* @param  		None
* @retval 		None
*/
void DMA1_Channel1_IRQHandler() {
	/* Invoke corresponding hook function for half/complete transfer */
	if (DMA_GetITStatus(DMA1_FLAG_HT1) == SET) {
		ADC_HookFunc_DMA_IT_HT();
	}
	if (DMA_GetITStatus(DMA1_FLAG_TC1) == SET) {
		ADC_HookFunc_DMA_IT_TC();
	}
	DMA_ClearITPendingBit(DMA1_IT_GL1);	// Clear all interrupt flags for DMA channel 1
}

